﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TechTree.Stalk.Roots.Exceptions;

namespace TechTree.Stalk.Roots.Cache
{
    public class CacheKey
    {
        #region Constantes
        private const string SEPARATOR = "@";
        private const string SEPARATOR_ARRAY_KEY = ",";
        private const string SEPARATOR_GROUP = ":";
        private const string DATA_FORMAT = "dd/MM/yyyy H:mm:ss";
        private const string DEFAULT_GROUP = "DEFAULT";
        private const string CHAR_GROUP = "[{0}]";

        private const string MESSAGE_ERROR_CACHE_KEY_TYPE = "Unable to create the cache key from type '{0}'.";
        private const string MESSAGE_ERROR_KEYS = "The set of keys must have more than one element.";
        #endregion

        #region Atributos
        private string _valor;
        private string _grupo;
        #endregion

        #region Propriedades
        public string Valor
        {
            get { return _valor; }
        }
        public string Grupo
        {
            get { return _grupo; }
        }
        #endregion

        #region Construtor
        public CacheKey(params object[] chaves_)
        {
            _grupo = DEFAULT_GROUP;
            _valor = ValidaChave(_grupo, chaves_);
        }

        public CacheKey(string grupo_, params object[] chaves_)
        {
            _grupo = grupo_;
            _valor = ValidaChave(grupo_, chaves_);
        }
        #endregion

        #region Métodos Privados
        private string ValidaChave(string grupo_, object[] chaves_)
        {
            List<String> listaChaves = new List<String>();

            for (int i = 0; i < chaves_.Length; i++)
            {
                Type tipoObjeto = chaves_[i].GetType();

                switch (tipoObjeto.BaseType.Name)
                {
                    case ("ValueType"):
                    case ("Object"):
                    case ("Array"):
                        {
                            switch (tipoObjeto.Name)
                            {
                                case ("DateTime"):
                                    {
                                        DateTime data = (DateTime)chaves_[i];
                                        listaChaves.Add(data.ToString(DATA_FORMAT));
                                        break;
                                    }
                                case ("Char"):
                                case ("String"):
                                case ("Boolean"):
                                case ("Int32"):
                                case ("Double"):
                                case ("Decimal"):
                                    {
                                        listaChaves.Add(chaves_[i].ToString());
                                        break;
                                    }
                                case ("Char[]"):
                                case ("String[]"):
                                case ("Boolean[]"):
                                case ("Int32[]"):
                                case ("Double[]"):
                                case ("Decimal[]"):
                                    {
                                        List<String> vetor = new List<String>();

                                        Array chaveVetor = (chaves_[i] as Array);

                                        for (int j = 0; j < chaveVetor.Length; j++)
                                        {
                                            vetor.Add(chaveVetor.GetValue(j).ToString());
                                        }

                                        listaChaves.Add("{" + String.Join(SEPARATOR_ARRAY_KEY, vetor.Select<String, String>(idAtr => idAtr.ToString()).ToArray()) + "}");
                                        break;
                                    }
                                default:
                                    {
                                        throw new CacheException(String.Format(MESSAGE_ERROR_CACHE_KEY_TYPE, tipoObjeto.Name));
                                    }
                            }
                            break;
                        }
                    case ("Enum"):
                        {
                            listaChaves.Add(chaves_[i].ToString());
                            break;
                        }
                    default:
                        {
                            throw new CacheException(String.Format(MESSAGE_ERROR_CACHE_KEY_TYPE, tipoObjeto.Name));
                        }
                }
            }

            string nomeGrupo = String.Format(CHAR_GROUP, grupo_);

            return nomeGrupo + SEPARATOR_GROUP + String.Join(SEPARATOR, listaChaves.Select<String, String>(idAtr => idAtr.ToString()).ToArray());
        }
        #endregion

        #region Métodos Protegidos
        public override string ToString()
        {
            return _valor;
        }
        #endregion
    }
}
